package com.edfu.core.commons.service.impl;

import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_EXISTS;
import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_ID_NOT_PRESENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_NAME_NOT_PRESENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_NOT_FOUND;
import static com.edfu.core.commons.exceptions.CommonExceptions.NULL_ARGUMENT;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edfu.core.commons.beans.Category;
import com.edfu.core.commons.exceptions.CategoryException;
import com.edfu.core.commons.exceptions.InvalidArgumentException;
import com.edfu.core.commons.persistence.api.ICategoryMgtPersistence;
import com.edfu.core.commons.persistence.entity.CategoryEntity;
import com.edfu.core.commons.service.api.ICategoryMgtService;
import com.edfu.core.commons.util.CategoryMgtBeanTransformer;

@Service
public class CategoryMgtServiceImpl implements ICategoryMgtService {
	
	private static Logger logger = LoggerFactory.getLogger(CategoryMgtServiceImpl.class);
	
	@Autowired
	private ICategoryMgtPersistence categoryMgtPersistence;

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { CategoryException.class })
	public Category createCategory(Category category) {
		logger.debug("Entered [createCategory] of [CategoryMgtServiceImpl]");
		
		checkNull(category);
		
		if (StringUtils.isBlank(category.getName()) || StringUtils.isEmpty(category.getName())) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}
		
		if (!categoryMgtPersistence.isCategoryUnique(category.getName())) {
			logger.warn("Category name already present in the DB");
			throw new InvalidArgumentException(CATEGORY_EXISTS);
		}
		
		//get parent category
		CategoryEntity parentCategory = null;
		CategoryEntity tce = CategoryMgtBeanTransformer.transformCategory(category);
		
		if (category.getParentCategory() == null || category.getName().equalsIgnoreCase(category.getParentCategory().getName())) {
			// there is no parent category in this case
			tce = categoryMgtPersistence.createCategory(tce);
			tce.setParentCategory(tce);
			categoryMgtPersistence.updateCategory(tce);
		} else {
			parentCategory = categoryMgtPersistence.getCategory(category.getParentCategory().getName());
			
			if (parentCategory == null) {
				logger.info("Category name " + category.getParentCategory().getName() + " doesn't exists in DB");
				throw new CategoryException(CATEGORY_NOT_FOUND);				
			}
			
			tce.setParentCategory(parentCategory);
			tce = categoryMgtPersistence.createCategory(tce);
		}
		
		if (category.getChildCategories() != null && category.getChildCategories().size() > 0) {
			CategoryEntity catEnt = null;
			for (Category tc : category.getChildCategories()) {
				if (StringUtils.isBlank(tc.getName()) || StringUtils.isEmpty(tc.getName())) {
					logger.warn("Child category name not present");
					throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
				}
				
				if (!categoryMgtPersistence.isCategoryUnique(tc.getName())) {
					logger.warn("Child category name " + tc.getName() + "already present in the DB");
					throw new InvalidArgumentException(CATEGORY_EXISTS);
				}
				catEnt = CategoryMgtBeanTransformer.transformCategory(tc);
				catEnt.setParentCategory(tce);
				categoryMgtPersistence.createCategory(catEnt);
			}
		}
		
		logger.debug("Exiting [createCategory] of [CategoryMgtServiceImpl]");
		return CategoryMgtBeanTransformer.transformCategory(tce);
		
	}
	
	private void checkNull(Object obj) {
		if (obj == null) {
			throw new InvalidArgumentException(NULL_ARGUMENT);
		}
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { CategoryException.class })
	public Category updateCategory(Category category) {
		logger.debug("Entered [updateCategory] of [CategoryMgtServiceImpl]");
		checkNull(category);
		validateCategoryId(category.getId());
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(category.getId());
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		if (category.getName() != null && !category.getName().equalsIgnoreCase(tce.getName()) && !categoryMgtPersistence.isCategoryUnique(category.getName())) {
			// Category Name is changed and changed category name already present in the DB in this case
			logger.warn("Category name " + category.getName() + " already present in the DB");
			throw new CategoryException(CATEGORY_EXISTS);
		}
		
		tce = CategoryMgtBeanTransformer.transformCategory(tce, category);
		
		tce = categoryMgtPersistence.updateCategory(tce);
		logger.debug("Exiting [updateCategory] of [CategoryMgtServiceImpl]");
		
		return CategoryMgtBeanTransformer.transformCategory(tce);
	}

	private void validateCategoryId(long categoryId) {
		if (categoryId == 0) {
			logger.warn("Category ID is not present");
			throw new InvalidArgumentException(CATEGORY_ID_NOT_PRESENT);
		}
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { CategoryException.class })
	public void removeCategory(long categoryId) {
		logger.debug("Entered [removeCategory] of [CategoryMgtServiceImpl]");
		validateCategoryId(categoryId);
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(categoryId);
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		categoryMgtPersistence.removeCategory(tce);
		logger.debug("Exiting [removeCategory] of [CategoryMgtServiceImpl]");
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { CategoryException.class })
	public void removeCategory(String categoryName) {
		logger.debug("Entered [removeCategory] of [CategoryMgtServiceImpl]");
		
		if (StringUtils.isBlank(categoryName) || StringUtils.isEmpty(categoryName)) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(categoryName);
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		categoryMgtPersistence.removeCategory(tce);
		logger.debug("Exiting [removeCategory] of [CategoryMgtServiceImpl]");
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Category> getCategories() {
		logger.debug("Entered [getCategories] of [CategoryMgtServiceImpl]");
		
		List<CategoryEntity> tceList = categoryMgtPersistence.getCategories();
		List<Category> tcList = null;
		if (tceList != null && tceList.size() > 0) {
			tcList = CategoryMgtBeanTransformer.transformCategory(tceList);
		}
		
		return CategoryMgtBeanTransformer.getHierarchicalCategories(tcList);
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Category getCategory(long categoryId) {
		logger.debug("Entered [getCategory] of [CategoryMgtServiceImpl]");
		
		validateCategoryId(categoryId);
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(categoryId);
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		
		return CategoryMgtBeanTransformer.transformCategory(tce);
	}
	
	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Category getCategory(String categoryName) {
		logger.debug("Entered [getCategory] of [CategoryMgtServiceImpl]");
		
		if (StringUtils.isBlank(categoryName) || StringUtils.isEmpty(categoryName)) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(categoryName);
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		
		return CategoryMgtBeanTransformer.transformCategory(tce);
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { CategoryException.class })
	public List<Category> addCategories(String parentCategoryName, List<Category> categories) {
		logger.debug("Entered [addCategories] of [CategoryMgtServiceImpl]");
		
		if (StringUtils.isBlank(parentCategoryName) || StringUtils.isEmpty(parentCategoryName)) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(parentCategoryName);
		List<CategoryEntity> categoryList = null;
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		
		if (categories != null && categories.size() > 0) {
			categoryList = categoryMgtPersistence.addCategories(tce, CategoryMgtBeanTransformer.transformCategoryBeans(categories));
		}		
		
		logger.debug("Exiting [addCategories] of [CategoryMgtServiceImpl]");
		
		return CategoryMgtBeanTransformer.transformCategory(categoryList);
	}

}
